﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization;

namespace Mimoza.Transport
{
    class UdpConnection : Connection
    {
        public UdpConnection(string remoteIpOrHostName, int port) :
            base(remoteIpOrHostName, port)
        {   
        }

        public void Connect()
        {
            CreateUdpClient(null);

            if (!IsEndPointBroadcast())
            {
                m_udpClient.Connect(m_endPoint);
            }

            FillSocket(m_udpClient.Client);

            Common.Logger.Log.Info("Connect UDP socket..OK.");
        }

        public void Bind()
        {
            CreateUdpClient(m_endPoint);
            FillSocket(m_udpClient.Client);

            Common.Logger.Log.Info("Bind UDP socket..OK.");
        }

        override public string RemoteAddress
        {
            get
            {
                return (m_endPoint!=null)?m_endPoint.Address.ToString():"";
            }
        }

        override protected void CreateConnection(IPEndPoint remoteEndPoint)
        {
            m_endPoint = remoteEndPoint;
        }

        override public void Send(Packet data, IPEndPoint remoteEndPoint)
        {
            MemoryStream stream = new MemoryStream();
                       
            data.Serialize(stream);

            if (IsEndPointBroadcast()) remoteEndPoint = m_endPoint;
 
            byte[] arrayToSend = stream.ToArray();
            m_udpClient.Send(arrayToSend, arrayToSend.Length, remoteEndPoint);

            stream.Close();
        }

        override public void Recieve(out Packet data, out IPEndPoint remoteEndPoint)
        {
            data = null; remoteEndPoint = null;

            if (IsEndPointBroadcast()) return;

            if (m_recieveStream == null)
            {
                m_recieveStream = new MemoryStream();
            }

            IPEndPoint endPoint = new IPEndPoint(m_endPoint.Address,m_endPoint.Port);
            byte[] arrayRecv = m_udpClient.Receive(ref endPoint);

            m_recieveStream.Write(arrayRecv, 0, arrayRecv.Length);
            m_recieveStream.Seek(-arrayRecv.Length, SeekOrigin.Current);

            data = Packet.Deserialize(m_recieveStream);

            remoteEndPoint = endPoint;
        }

        override public void Close()
        {
            if (m_udpClient != null)
            {
                Common.Logger.Log.Info("Close connection - " + RemoteAddress + ".");

                m_udpClient.Close();
            }

            SetCloseFlag();
        }

        void CreateUdpClient(IPEndPoint endPoint)
        {
            if (endPoint==null) m_udpClient = new UdpClient();
            else m_udpClient = new UdpClient(endPoint);

            if (IsEndPointBroadcast())
            {
                m_udpClient.Client.SetSocketOption(SocketOptionLevel.Socket,
                    SocketOptionName.Broadcast, 1);
            }
        }

        bool IsEndPointBroadcast()
        {
            bool bIsBroadcast = (m_endPoint.Address.Equals(IPAddress.Broadcast));

            Common.Logger.Log.Info("UDPConnection.IsEndPointBroadcast - " + bIsBroadcast.ToString() + 
                " endPoint - '" + m_endPoint.ToString() + "'.");

            return bIsBroadcast;
        }

        UdpClient m_udpClient;
        IPEndPoint m_endPoint;
        MemoryStream m_recieveStream;
    }
}
